/*
 * File:   main.cpp
 * Author: Josué
 *
 * Created on October 28, 2009, 1:27 PM
 */

#include <GL/glui.h>
#include <iostream>

#include "pixelMap.h"

///Acoes GLUI
#define LOAD_IMAGE      0x01
#define AUTO_TH      0x02

void idleFunc(void);
void pictureDisplay(void);
void pictureInit(void);
void initMatrixes();
void reloadImage();


void mouse(int button, int state, int x, int y);
void motionMouse(int x, int y);

void histogramInit();
void histogramDisplay();
void setHistogram();

void resultInit();
void resultDisplay();

void calcResult(int line);

///Funcoes GLUI
void controlCallback(int control);

void setPictures(char* img);

int winMain, winControl, winResult;
int maxSize = 0;
int avg = 0;
int linex = 127;
bool moving = false;
int delta = 3;
int th1, th2;

int newTh();
void automaticCalc();

PixelMap* pixelMap;
PixelMap* pixelMapResult;
PixelMap* imageMap;
pixel** pixels;
pixel** resultPixels;

int histogram[256];

uByte *data;
int LBLP_Size = 0;

///Variaveis do GLUI
char image[30] = "cube.bmp";

GLUI* glui;
GLUI_Panel* panelMain;

GLUI_Panel* panelImage;
GLUI_EditText *editImage;
GLUI_Button* buttonImage;
GLUI_Button* autoTh;

int main(int argc, char** argv) {
    setPictures(image);
    initMatrixes();
    glutInit(&argc, argv);

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(pixelMap->GetWidth(), pixelMap->GetHeight());
    glutInitWindowPosition(100, 100);
    winMain = glutCreateWindow("Imagem");
    glutDisplayFunc(pictureDisplay);
    pictureInit();

    glui = GLUI_Master.create_glui("Control", 0, 1000, 100);
    panelMain = glui->add_panel("", GLUI_PANEL_NONE);

    panelImage = glui->add_panel_to_panel(panelMain, "Image");
    editImage = glui->add_edittext_to_panel(panelImage, "Image: ");
    buttonImage = glui->add_button_to_panel(panelImage, "Set", LOAD_IMAGE, controlCallback);
    autoTh = glui->add_button_to_panel(panelImage, "Auto Threshold", AUTO_TH, controlCallback);

    glui->set_main_gfx_window(winMain);
    GLUI_Master.set_glutIdleFunc(idleFunc);


    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
    glutInitWindowSize(768, 400);
    glutInitWindowPosition(256, 256);
    winControl = glutCreateWindow("Histogram Window");
    glutDisplayFunc(histogramDisplay);
    glutMouseFunc( mouse );
    glutMotionFunc( motionMouse );
    histogramInit();


    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(pixelMap->GetWidth(), pixelMap->GetHeight());
    glutInitWindowPosition(600, 600);
    winResult = glutCreateWindow("Resultado");
    glutDisplayFunc(resultDisplay);
    resultInit();

    //    glutMouseFunc( mouse );
    //    glutMotionFunc( motionMouse );



    glutMainLoop();
    return 0;
}

void idleFunc(void) {
    if (glutGetWindow() != winMain) glutSetWindow(winMain);
    glutPostRedisplay();
}

void pictureDisplay(void) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    pixelMap->ViewImage();
    glutSwapBuffers();
    glutPostRedisplay();
}

void pictureInit(void) {
    // selecionar cor de fundo (preto)
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
    glLoadIdentity(); // Inicializa com matriz identidade
}

void resultDisplay(void) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    pixelMapResult->ViewImage();
    glutSwapBuffers();
    glutPostRedisplay();
}

void resultInit(void) {
    // selecionar cor de fundo (preto)
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
    glLoadIdentity(); // Inicializa com matriz identidade
}

void setPictures(char* img) {
    pixelMap = new PixelMap(img);
    pixelMap->ConvertToGrayScale();
    pixelMapResult = new PixelMap(img);
    pixelMapResult->ConvertToGrayScale();
    imageMap = pixelMap;
}

void controlCallback(int control) {
    switch (control) {
        case LOAD_IMAGE:
        {
            reloadImage();
            break;
        }
        case AUTO_TH:
        {
            automaticCalc();
        }
        default:
            break;
    }
}

void initMatrixes() {
    pixels = (pixel **) malloc(pixelMap->GetHeight() * sizeof (pixel *));
    for (int i = 0; i < pixelMap->GetHeight(); i++) {
        pixels[i] = (pixel *) malloc(pixelMap->GetWidth() * sizeof (pixel));
    }
    resultPixels = (pixel **) malloc(pixelMap->GetHeight() * sizeof (pixel *));
    for (int i = 0; i < pixelMap->GetHeight(); i++) {
        resultPixels[i] = (pixel *) malloc(pixelMap->GetWidth() * sizeof (pixel));
    }
    pixelMap->ConvertDataToPixels(pixels);
    pixelMapResult->ConvertDataToPixels(pixels);

}

void freeMatrixes() {
    for (int i = 0; i < pixelMap->GetHeight(); i++) {
        free(pixels[i]);
        free(resultPixels[i]);
    }
    free(pixels);
    free(resultPixels);
}

void reloadImage() {
    freeMatrixes();
    pixelMap->Read(editImage->get_text());
    pixelMap->ConvertToGrayScale();
    initMatrixes();
    glutReshapeWindow(pixelMap->GetWidth(), pixelMap->GetHeight());
}

void histogramInit() {

    glClearColor(0.0, 0.0, 0.0, 1.0);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0.0, 767.0, 0.0, 255.0, -1.0, 1.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    for (int i = 0; i < 256; i++) {
        histogram[i] = 0;
    }

    setHistogram();
}

void setHistogram() {
    for (int i = 0; i < pixelMap->GetHeight(); i++) {
        for (int j = 0; j < pixelMap->GetWidth(); j++) {
            histogram[pixels[i][j].value]++;
        }
    }
    maxSize = 0;
    avg = 0;
//    int k;
    for (int i = 0; i <= 255; i++) {
        if(histogram[i] > maxSize) maxSize = histogram[i];
        avg += (i*histogram[i]);
        if (i > 254) std::cout << "i: " << i << std::endl;
    }
    avg /= (pixelMap->GetWidth()*pixelMap->GetHeight());
    th1 = avg;
    th2 = avg;
    printf("%d\n", avg);

}

void drawCLine(){
    glBegin(GL_LINES);
    glColor3f(0.f, 1.f, 0.f);
    glVertex2i(linex*3, 0);
    glVertex2i(linex*3, 400);
    glEnd();
}

void histogramDisplay() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glBegin(GL_LINES);
    for (int i = 0; i < 256; i++) {
        glColor3f(1.0f, 1.0f, 1.0f);
        glVertex2f((1.0f + (i * 3.0f)), 0.0);
        glVertex2f((1.0f + (i * 3.0f)), (((float)histogram[i]/(float)maxSize)*400.0));
    }
    glEnd();
//    glMatrixMode(GL_MODELVIEW);
//    glLoadIdentity();
//    glScalef(1.f, 5.f, 5.f);
    drawCLine();
    glutSwapBuffers();
    glutPostRedisplay();
}


void mouse(int button, int state, int x, int y)
{
    if (button == GLUT_LEFT_BUTTON)
    {
        if (state == GLUT_DOWN)
        {
            if (!moving)
            {
                    float xInf = (linex*3) - 5;
                    float xSup = (linex*3) + 5;

                    if ((x > xInf) && (x < xSup))
                    {
                        moving = true;
                    }
                }
            }
        }
    if (state == GLUT_UP)
        {
            if (moving)
            {
//                recalcPixels();
                moving = false;
            }
             glutPostRedisplay();
        }
}


void motionMouse(int x, int y)
{
    if (moving)
    {
            if (x >= 0 && y <= 767)
            {
                linex = (int)(x / 3);
                calcResult(linex);
            }
        glutPostRedisplay();
    }
}

void calcResult(int line){
    for(int i = 0; i < pixelMap->GetHeight(); i++){
        for(int j = 0; j < pixelMap->GetWidth(); j++){
            if(((int)pixels[i][j].value) < line) resultPixels[i][j].value = (uByte)0;
            else resultPixels[i][j].value = (uByte)255;
        }
    }
    pixelMapResult->ConvertPixelsToData(resultPixels);
    glutPostRedisplay();
}

void automaticCalc(){
    int delta1;

    do{
        th1 = th2;//    printf("%d, %d, \n", avg1, avg2);

        th2 = newTh();
        calcResult(th2);
        delta1 = th2-th1;
        if(delta1 < 0) delta1 *= -1;
    } while( delta1 > delta);
}

int newTh(){
    int avg1 = 0, avg2 = 0, a1 = 0, a2 = 0;
    for(int i = 0; i < th2; i++){
        avg1 += (i*histogram[i]);
        a1 += histogram[i];
    }
    for(int i = th2; i < 256; i++){
        avg2 += (i*histogram[i]);
        a2 += histogram[i];
    }
    avg1 /= a1;
    avg2 /= a2;
//    printf("%d, %d, \n", avg1, avg2);
    return (int)((avg1+avg2)/2);
}
